package com.virjar.ratel.builder.helper.buildenv;

import com.virjar.ratel.allcommon.ReflectUtil;

import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.time.LocalDateTime;
import java.util.Map;

/**
 * 重新编译一个class，类似Android实现BuildConfig的功能，主要用于在常量中定义环境。
 * 这样我们可以通过静态变量确定一些flag，而这些flag可以帮助代码优化器进行代码优化,这样进一步减少输出的资源大小<br>
 * 如下flag:
 * 1. debug
 * 2. 平台：pc/android
 * 3. 全局同步静态常量,版本号等
 */
public class ReCompileClass {
    public static byte[] recompile(Class<?> clazz) throws IOException {
        String sourceCode = reGenPlainClass(clazz);
        Map<String, byte[]> compileResult = new JavaStringCompiler().compile(clazz.getSimpleName() + ".java", sourceCode);
        return compileResult.values().iterator().next();
    }

    private static String reGenPlainClass(Class<?> clazz) {
        StringBuilder code = new StringBuilder("package ");

        code.append(clazz.getPackage().getName()).append(";\n");

        code.append("/**\n"
                + " * 请注意，当前class的所有字段必须是final的常量，在混淆工具链中本class会被删除\n * "
                + "\n * 当前为自动混淆产生class "
                + "\n * auto generated by ratel  on ")
                .append(LocalDateTime.now().toString())
                .append("\n */\n");

        code.append("public class ").append(clazz.getSimpleName()).append(" {\n");

        Field[] declaredFields = clazz.getFields();
        for (Field field : declaredFields) {
            if (!Modifier.isStatic(field.getModifiers())
                    || !Modifier.isPublic(field.getModifiers())
            ) {
                System.out.println("error field: " + field);
                continue;
            }

            code.append("   public static final ");
            code.append(field.getType().getName());
            code.append(" ").append(field.getName()).append(" = ");
            if (field.getType().equals(String.class)) {
                String str = ReflectUtil.getStaticFiled(clazz, field.getName());
                code.append(quote(str));
            } else {
                code.append((Object) ReflectUtil.getStaticFiled(clazz, field.getName()));
                if (field.getType().equals(long.class) || field.getType().equals(Long.class)) {
                    code.append("L");
                }
            }
            code.append(";\n");
        }
        code.append("}");

        return code.toString();
    }

    private static String quote(String input) {
        SerializeWriter serializeWriter = new SerializeWriter();
        serializeWriter.writeString(input);
        return serializeWriter.toString();
    }
}
